package com.shop.aop;

import com.alibaba.fastjson.JSON;
//import groovy.util.logging.Slf4j;

import com.shop.entity.OperLog;
import com.shop.entity.User;
import com.shop.utils.HttpContextUtil;
import com.shop.utils.IpUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import com.shop.annotation.MyLog;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.shop.service.OperLogService;

/**
 * 切面处理类，记录操作日志到数据库
 */
@Aspect
@Component
@Slf4j
public class OperLogAspect {

    @Autowired
    private OperLogService operLogService;

    String uname = null;
    String aname = null;
    String sname = null;

    /**
     * 设置操作日志切入点，这里介绍两种方式：
     * 1、基于注解切入（也就是打了自定义注解的方法才会切入）
     *    @Pointcut("@annotation(com.shop.annotation.MyLog)")
     * 2、基于包扫描切入
     *    @Pointcut("execution(public * com.shop.controller..*.*(..))")
     */
    @Pointcut("@annotation(com.shop.annotation.MyLog)")//在注解的位置切入代码
    //@Pointcut("execution(public * org.wujiangbo.controller..*.*(..))")//从controller切入
    public void operLogPoinCut() {
    }

    @Before("operLogPoinCut()")
    public void beforMethod(JoinPoint point){
        HttpServletRequest request= HttpContextUtil.getHttpServletRequest();
        uname = (String) request.getSession().getAttribute("username");
        aname = (String) request.getSession().getAttribute("adminname");
        sname = (String) request.getSession().getAttribute("salesname");
    }

    /**
     * 设置操作异常切入点记录异常日志 扫描所有web包下操作
     */
    @Pointcut("execution(* com.shop.web..*.*(..))")
    public void operExceptionLogPoinCut() {
    }

    /**
     * 正常返回通知，拦截用户操作日志，连接点正常执行完成后执行， 如果连接点抛出异常，则不会执行
     *
     * @param joinPoint 切入点
     */
    
    @Around("operLogPoinCut()")
    public Object saveOperLog(ProceedingJoinPoint joinPoint) throws Throwable {
        OperLog operLog=new OperLog();
        long startTime= System.currentTimeMillis();
        Object result = joinPoint.proceed();
        // 从切面织入点处通过反射机制获取织入点处的方法
        long takeTime = System.currentTimeMillis() - startTime;//记录方法执行耗时时间（单位：毫秒）
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取切入点所在的方法
        Method method = signature.getMethod();
        // 获取操作
        MyLog myLog = method.getAnnotation(MyLog.class);
        log.info("-----------------------log start--------------------");

        operLog.setTitle(myLog.title());
        log.info("title:{}",operLog.getTitle());

        operLog.setContent(myLog.content());
        log.info("content:{}", operLog.getContent());

        HttpServletRequest request= HttpContextUtil.getHttpServletRequest();
        operLog.setIp(IpUtil.getIpAddr(request));
        log.info("ip:{}", operLog.getIp());

        Date opertime =new Date();
        operLog.setOperTime(opertime);
        log.info("time:{}", operLog.getOperTime());


        operLog.setTakeTime(takeTime);
        log.info("execute time:{} ms", operLog.getTakeTime());

        if(request.getSession().getAttribute("username")!=null){
        uname = (String) request.getSession().getAttribute("username");
        }
        if( request.getSession().getAttribute("adminname")!=null){
        aname = (String) request.getSession().getAttribute("adminname");}
        if( request.getSession().getAttribute("salesname")!=null){
            sname = (String) request.getSession().getAttribute("salesname");}
        if (aname!=null){

            operLog.setOperName(aname);
            log.info("id:{}",operLog.getOperName());

        }else if(uname!=null){

            operLog.setOperName(uname);
            log.info("id:{}",operLog.getOperName());
        }
        else {
            operLog.setOperName(sname);
            log.info("id:{}",operLog.getOperName());
        }
        log.info("-----------------------log end--------------------");
        operLogService.create(operLog);
        return result;
    }

    /**
     * 异常返回通知，用于拦截异常日志信息 连接点抛出异常后执行
     */
    /*@AfterThrowing(pointcut = "operExceptionLogPoinCut()", throwing = "e")
    public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);

        OperLog operlog = new OperLog();
        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            // 获取请求的方法名
            String methodName = method.getName();
            methodName = className + "." + methodName + "()";
            // 获取操作
            MyLog myLog = method.getAnnotation(MyLog.class);
            if (myLog != null) {
                operlog.setTitle(myLog.title());//设置模块名称
                operlog.setContent(myLog.content());//设置日志内容
            }
            // 将入参转换成json
            operlog.setOperName("张三"); // 获取用户名（真实环境中，肯定有工具类获取当前登录者的账号或ID的，或者从token中解析而来）
            operlog.setIp(getIp(request)); // IP地址
            operlog.setOperTime(new Date()); // 时间
            //插入数据库
            operLogService.insert(operlog);
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }*/

    /**
     * 转换异常信息为字符串
     */
    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuffer strbuff = new StringBuffer();
        for (StackTraceElement stet : elements) {
            strbuff.append(stet + "\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
        message = substring(message,0 ,2000);
        return message;
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray)
    {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0)
        {
            for (Object o : paramsArray)
            {
                if (o != null)
                {
                    try
                    {
                        Object jsonObj = JSON.toJSON(o);
                        params += jsonObj.toString() + " ";
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        }
        return params.trim();
    }

    //字符串截取
    public static String substring(String str, int start, int end) {
        if (str == null) {
            return null;
        } else {
            if (end < 0) {
                end += str.length();
            }

            if (start < 0) {
                start += str.length();
            }

            if (end > str.length()) {
                end = str.length();
            }

            if (start > end) {
                return "";
            } else {
                if (start < 0) {
                    start = 0;
                }

                if (end < 0) {
                    end = 0;
                }
                return str.substring(start, end);
            }
        }
    }

    /**
     * 转换request 请求参数
     * @param paramMap request获取的参数数组
     */
    public Map<String, String> converMap(Map<String, String[]> paramMap) {
        Map<String, String> returnMap = new HashMap<>();
        for (String key : paramMap.keySet()) {
            returnMap.put(key, paramMap.get(key)[0]);
        }
        return returnMap;
    }
}
